/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.rendering.test.integration; import java.io.StringReader; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.ComparisonFailure; import org.junit.Test; import org.xwiki.component.manager.ComponentManager; import org.xwiki.configuration.ConfigurationSource; import org.xwiki.rendering.block.XDOM; import org.xwiki.rendering.listener.MetaData; import org.xwiki.rendering.listener.WrappingListener; import org.xwiki.rendering.parser.Parser; import org.xwiki.rendering.parser.StreamParser; import org.xwiki.rendering.renderer.BlockRenderer; import org.xwiki.rendering.renderer.PrintRendererFactory; import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter; import org.xwiki.rendering.renderer.printer.WikiPrinter; import org.xwiki.rendering.syntax.SyntaxFactory; import org.xwiki.rendering.transformation.TransformationContext; import org.xwiki.rendering.transformation.TransformationManager; import org.xwiki.test.internal.MockConfigurationSource; /** * A generic JUnit Test used by {@link RenderingTestSuite} to parse some passed content and verify it matches some * passed expectation. The format of the input/expectation is specified in {@link TestDataParser}. * * @version $Id: e73d144e6c91ce97e8d0adec695d03c2fe066a10 $ * @since 3.0RC1 */ public class RenderingTest { private String input; private String expected; private String parserId; private String targetSyntaxId; private boolean streaming; private boolean runTransformations; private Map<String, ?> configuration; private ComponentManager componentManager; public RenderingTest(String input, String expected, String parserId, String targetSyntaxId, boolean streaming, boolean runTransformations, Map<String, ?> configuration, ComponentManager componentManager) { this.input = input; this.expected = expected; this.parserId = parserId; this.targetSyntaxId = targetSyntaxId; this.streaming = streaming; this.runTransformations = runTransformations; this.configuration = configuration; this.componentManager = componentManager; } @Test public void execute() throws Throwable { Map<String, String> originalConfiguration = new HashMap<String, String>(); if (this.configuration != null) { ConfigurationSource configurationSource = getComponentManager().getInstance(ConfigurationSource.class); if (configurationSource instanceof MockConfigurationSource) { MockConfigurationSource mockConfigurationSource = (MockConfigurationSource) configurationSource; for (Map.Entry<String, ?> entry : this.configuration.entrySet()) { originalConfiguration.put(entry.getKey(), mockConfigurationSource.<String>getProperty(entry.getKey())); mockConfigurationSource.setProperty(entry.getKey(), entry.getValue()); } } } try { runTestInternal(); } finally { // Revert Configuration that have been set if (this.configuration != null) { ConfigurationSource configurationSource = getComponentManager().getInstance(ConfigurationSource.class); if (configurationSource instanceof MockConfigurationSource) { MockConfigurationSource mockConfigurationSource = (MockConfigurationSource) configurationSource; for (Map.Entry<String, String> entry : originalConfiguration.entrySet()) { if (entry.getValue() == null) { mockConfigurationSource.removeProperty(entry.getKey()); } else { mockConfigurationSource.setProperty(entry.getKey(), entry.getValue()); } } } } } } private void runTestInternal() throws Throwable { WikiPrinter printer = new DefaultWikiPrinter(); if (!this.streaming) { Parser parser = getComponentManager().getInstance(Parser.class, this.parserId); XDOM xdom = parser.parse(new StringReader(this.input)); if (this.runTransformations) { SyntaxFactory syntaxFactory = getComponentManager().getInstance(SyntaxFactory.class); TransformationManager transformationManager = getComponentManager().getInstance(TransformationManager.class); TransformationContext txContext = new TransformationContext(xdom, syntaxFactory.createSyntaxFromIdString(this.parserId)); txContext.setTargetSyntax(syntaxFactory.createSyntaxFromIdString(this.targetSyntaxId)); txContext.setId("test"); transformationManager.performTransformations(xdom, txContext); } BlockRenderer renderer = getComponentManager().getInstance(BlockRenderer.class, this.targetSyntaxId); // remove source syntax from XDOM metadata if (xdom.getMetaData() != null) { Map<String, Object> metadataMap = new HashMap<String, Object>(xdom.getMetaData().getMetaData()); metadataMap.remove(MetaData.SYNTAX); xdom = new XDOM(xdom.getChildren(), new MetaData(metadataMap)); } renderer.render(xdom, printer); } else { StreamParser parser = getComponentManager().getInstance(StreamParser.class, this.parserId); PrintRendererFactory printRendererFactory = getComponentManager().getInstance(PrintRendererFactory.class, this.targetSyntaxId); // remove source syntax from begin/endDocument metadata WrappingListener listener = new SyntaxWrappingListener(); listener.setWrappedListener(printRendererFactory.createRenderer(printer)); parser.parse(new StringReader(this.input), listener); } // Verify the expected result against the result we got. assertExpectedResult(this.expected, printer.toString()); } /** * Compare the passed expected string with the passed result. * We support regexes for comparison usng the format: ${{{regex:...}}}. For example: * <code> * .#----------------------------------------------------- * .expect|event/1.0 * .#----------------------------------------------------- * beginDocument * beginMacroMarkerStandalone [useravatar] [username=XWiki.UserNotExisting] * beginGroup [[class]=[xwikirenderingerror]] * onWord [Failed to execute the [useravatar] macro] * endGroup [[class]=[xwikirenderingerror]] * beginGroup [[class]=[xwikirenderingerrordescription hidden]] * onVerbatim [org.xwiki.rendering.macro.MacroExecutionException: User [XWiki.UserNotExisting]${{{regex:.*}}}] * endGroup [[class]=[xwikirenderingerrordescription hidden]] * endMacroMarkerStandalone [useravatar] [username=XWiki.UserNotExisting] * endDocument * </code> */ private void assertExpectedResult(String expected, String result) { StringBuilder builder = new StringBuilder(); normalizeExpectedValue(builder, expected); Pattern pattern = Pattern.compile(builder.toString(), Pattern.DOTALL); Matcher matcher = pattern.matcher(result); if (!matcher.matches()) { throw new ComparisonFailure("", expected, result); } } private void normalizeExpectedValue(StringBuilder builder, String expected) { int pos = expected.indexOf("${{{regex:"); if (pos > -1) { builder.append(Pattern.quote(expected.substring(0, pos))); // Find end of regex definition int pos2 = expected.indexOf("}}}", pos + 10); if (pos2 == -1) { throw new RuntimeException("Invalid regex declaration: missing closing part }}}"); } builder.append(expected.substring(pos + 10, pos2)); normalizeExpectedValue(builder, expected.substring(pos2 + 3)); } else { builder.append(Pattern.quote(expected)); } } public ComponentManager getComponentManager() throws Exception { return this.componentManager; } }